/*
 * Copyright the original author or authors.
 */
package com.sihai.core.support;

import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.impl.CriteriaImpl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;


/**
 * <code>CriteriaTotalCountGetter</code> 使用Hibernate {@link org.hibernate.Criteria} 获得总记录数
 *
 * @author snowway
 * @since 2009-2-18
 */
public class CriteriaTotalCountGetter implements TotalcountGetter {

    private static final Log logger = LogFactory.getLog(CriteriaTotalCountGetter.class);

    private Criteria criteria;

    public CriteriaTotalCountGetter(Session session, Criteria criteria) {
        if (!(criteria instanceof Serializable)) {
            throw new RuntimeException("Criteria:" + criteria + " is not serializable");
        }
        Criteria countCriteria = (Criteria) SerializationUtils.clone((Serializable) criteria);
        countCriteria.setProjection(Projections.rowCount());
        CriteriaImpl cimpl;

        if (countCriteria instanceof CriteriaImpl) {
            cimpl = ((CriteriaImpl) countCriteria);
        } else if (countCriteria instanceof CriteriaImpl.Subcriteria) {
            cimpl = (CriteriaImpl) ((CriteriaImpl.Subcriteria) countCriteria).getParent();
        } else {
            throw new RuntimeException("unknown Criteria implementation:" + countCriteria);
        }

        //去除order by
        Field field;
        try {
            field = cimpl.getClass().getDeclaredField("orderEntries");
            field.setAccessible(true);
            field.set(cimpl, new ArrayList());
        } catch (Exception e) {
            if (logger.isWarnEnabled()) {
                logger.warn("can not rewrite hibernate CriteriaImpl's orderEntries", e);
            }
        }
        if (!(session instanceof SessionImplementor)) {
            throw new RuntimeException("unknown Session implementation:" + session + ", session should be inherit from SessionImplementor");
        }
        cimpl.setSession(SessionImplementor.class.cast(session));
        this.criteria = cimpl;
    }

    @Override
    public int count() {
        criteria.setProjection(Projections.rowCount());
        return ((Number) this.criteria.uniqueResult()).intValue();
    }
}
